// Filename:    RequestListener.cpp
// Author:      John Van Drasek
// Date:		4/2/2011
// Description:


// LUA
extern"C"
{
#include <lua.h>
#include <lauxlib.h>
#include <lualib.h>
}
// RUP
#include "RUP.h"
// Threading
#include "pthread.h"
// Application
#include "AJ_Globals.h"
#include "AJ_RequestListener.h"

pthread_t tRequestListener;
int requestListenerSock;

bool AJ_cbOnCompletedJob()
{
    printf("C++ side: AJ_cbOnCompletedJob callback called.\n");

    double z;
    int result = -1;

    // Create a lua state object
    lua_State* aj_luaState = lua_open();

    // Open all standard Lua libraries into the lua state variable
    luaL_openlibs(aj_luaState);

    // Attempt to load a file as a Lua chunk
    result = luaL_loadfile(aj_luaState, "Callbacks.lua");
    if(result != 0)
    {
        // Some error occured from loading the lua file.  If the file doesn't exist, just
        // print warning and proceed as if the user has not specified any callback functions.
        printf("WARNING: %s\n", lua_tostring(aj_luaState, -1));
        return false;
    }        

    // Check to see if their are any errors in the file, if so report them and skip executing it
    result = lua_pcall(aj_luaState, 0, 0, 0);
    if(result != 0)
    {
        printf("ERROR: %s\n", lua_tostring(aj_luaState, -1));
        return false;
    }
    
    // Find the function name to be called and push it onto the lua stack
    lua_getglobal(aj_luaState, "AJ_cbOnCompletedJob");

    // checks to see if the string represents a function
    result = lua_isfunction(aj_luaState,-1);
    if(result == 0)
    {
        // Pop the invalid data off the stack
        lua_pop(aj_luaState,1);
        // The callback was not found in the script file, so either the user didn't
        // specify one, or they specified the wrong name for this callback function.
        // Print warning to let them know we looked but didn't find what we were
        // looking for.
        printf("WARNING: AJ_cbOnCompletedJob - is not in the script file.");
        return false;
    }

    ////
    //// NOTE: this is an example of how to push arguments to a lua script
    ////
    //// Push function arguments on to the lua stack
    //lua_pushnumber(aj_luaState, 21);
    //lua_pushnumber(aj_luaState, 31);
    //
    //// Call the function with 2 arguments and 1 return value
    //if (lua_pcall(aj_luaState, 2, 1, 0) != 0)
    //
    //// Get the result from the function, we are expecting a return value from this example
    //if (!lua_isnumber(aj_luaState, -1))
    //{
    //    printf("function `AJ_cbOnCompletedJob' must return a number\n");
    //}
    //z = lua_tonumber(aj_luaState, -1);

    // Call the function and check to see if it succeeds
    result = lua_pcall(aj_luaState, 0, 0, 0);
    if (result != 0)
    {
        // The function call did not succeed, lets print an error to let the user know
        printf("ERROR: (AJ_cbOnCompletedJob) - %s\n",lua_tostring(aj_luaState, -1));
    }

    //z = lua_tonumber(aj_luaState, -1);
    //printf("Result: %f\n",z);
    lua_pop(aj_luaState, 1);
    lua_close(aj_luaState);
}

int RequestListener_Init()
{
	int result = AJ_FAIL;
	int threadID;
	int ret;
	threadID = 1;

	// Initializing the global size variable for logged in users 
	//sizeOfLoggedInUsersList = 0;

	// Create a list for logged in users
	//loggedInUsersList = new struct pktList;

	// Assign values to struct variables
	//loggedInUsersList->_head = 0;
	//strncpy(loggedInUsersList->_name, "loggedInUsersList", 18);
	//loggedInUsersList->_size = 0;

	ret = pthread_create(&tRequestListener, NULL, RequestListener, (void*)threadID);
	ret = pthread_detach(tRequestListener);

	return result;
}

int RequestListener_Destroy()
{
	int result = AJ_FAIL;
	//int i;
	//pktNode* curPktNode;
	//curPktNode = loggedInUsersList->_head;

	// Delete each item in the loggedInUsersList
	//for(i = sizeOfLoggedInUsersList; i > 0; --i)
	//{

		// get the address of the list item and remove from list
	//	if(RemovePktByAddress(loggedInUsersList,inet_ntoa(((struct pkt*)curPktNode->_data)->_client.sin_addr)))
	//	{
			//printf("Address removed successfully!\n");
	//	}
	//}

	// Delete the loggedInUsersList holding all logged in users
	//if(loggedInUsersList->_size == 0)
	//{
	//	delete loggedInUsersList;
	//}
	//else
	//{
	//	printf("The loggedInUsersList was not deleted because it wasn't empty!\n");
	//}

	if(!(pthread_cancel(tRequestListener)))
	{
		printf("Request Listener was stopped successfully!\n");
		rup_close(requestListenerSock); // FIXME: removed to get rid of compiler warning that stated it was unreachable code.
		result = AJ_SUCCESS;
	}
	else
	{
		printf("Something went wrong while trying to stop Request Listener!  :(\n");
		result = AJ_FAIL;
	}

	return result;
}

void* RequestListener(void* tID)
{
	int result, fromlen, numRows;
	struct sockaddr_in from;
	struct pkt inPkt;
	char query[QUERYSIZE];
	int chatSock;

	// New
	int cmdLineParamLen = 0;
	char* remoteIP = NULL;
	char* commands = NULL;

	result = 0;

	// Assign fromlen to the size of the sockaddr_in struct
	fromlen = sizeof(struct sockaddr_in);

	// Create INTERNET, udp datagram socket
	requestListenerSock = rup_open();

	// Bind protocol to socket
	if(rup_bind(requestListenerSock, SERVERLOGINPORT))
	{
		exit(1); 
	}

	// Open a socket to write chat back to the user
	// Create INTERNET, udp datagram socket
	chatSock = rup_open();

	// Bind protocol to socket
	if(rup_bind(chatSock, SERVERCHATPORT)) 
	{
		exit(1); 
	}

	// Loop forever reading and writing on port
	for(;;)
	{
		// clear the memory in the character arrays
		memset(query,0,QUERYSIZE);
		int rc;

		if((rc=rup_read(requestListenerSock, &inPkt, sizeof(struct pkt), &from)) < 0 )
		{
			printf("login server error: errno %d\n",errno);
			perror("reading datagram");
			exit(1);
		}

		if((strcmp("logout",inPkt._command) == 0))
		{
			strncpy((inPkt._msgbuf),"success",8);

		}
		else if((strcmp("login",inPkt._command) == 0))
		{
			strncpy((inPkt._msgbuf),"success",8);
		}
		else if(strcmp(inPkt._command,"execute") == 0)
		{

			// Store the data to execute
			cmdLineParamLen = strnlen(inPkt._msgbuf,BUFSIZE) + 1;  // Add 1 for null terminator
			commands = (char*)malloc(cmdLineParamLen);
			memset(commands, 0, cmdLineParamLen);
			strncpy(commands, inPkt._msgbuf, cmdLineParamLen);
			commands[cmdLineParamLen-1] = '\0'; // Set null terminator to last position

			strncpy((inPkt._msgbuf),"success",8);
		}

		// pkt._msgbuf contents has changed! Must calculate checksum again and
		//   assign to pkt so data transfer will succeed
		inPkt._checksum = performChecksum(&inPkt);

		// Write data back to the user
		if(rup_write(requestListenerSock,&inPkt,sizeof(struct pkt),&from) < 0 )
		{
			perror("login server sendto: ");

			if (errno == ENOBUFS)
				continue;

			exit(1);
		}

		// Assign the from address to the incoming pkt.
		inPkt._client.sin_addr = from.sin_addr;

		// Perform loggedInUsersList additions and removals after the pkt
		//  has been sent to avoid timing issues when trying to do it
		//  between the rup_read and rup_write calls
		if(strcmp(inPkt._command,"login") == 0)
		{
			// Add user to loggedInUsersList if msgbuf is equal to success
			if(strcmp(inPkt._msgbuf,"success") == 0)
			{
				//if(addUserToList(&inPkt))
				//{
				//	// Users was added to the list successfully
				//}
				//else
				//{
				//	printf("ERROR: User was not added to loggedInUsersList.\n");
				//}
			}
			else
			{
				printf("ERROR: User was not added to loggedInUsersList.\n");
			}
		}
		else if(strcmp(inPkt._command,"logout") == 0)
		{
			//if( removeUserFromList(&inPkt))
			//{
			//	printf("removed %s user from list.\n",inet_ntoa(inPkt._client.sin_addr));
			//}
		}
		else if(strcmp(inPkt._command,"execute") == 0)
		{
			//if(addUserToList(&inPkt))
			//{
			//	// Users was added to the list successfully
			//}
			//else
			//{
			//	printf("ERROR: User was not added to loggedInUsersList.\n");
			//}

			printf("RequestListener: Request received!!!\n");
			system(commands);
			free(commands);
			
			//printf("\n\n\nsending log back to users.....\n\n\n");

			// Write some contents to the buffer to send it back
			//char outBuffer[BUFSIZE] = "Data sent from server";
			//strncpy_s(inPkt._msgbuf,outBuffer, strlen(outBuffer));
			//inPkt._checksum = performChecksum(&inPkt);

			// Call the callback function
			result = AJ_cbOnCompletedJob();
			//result = false;

			// Create a new packet.  Send multiple packets to client.
			//struct pkt* outPkt;
			//outPkt = createPkt(1,"", &from,inPkt._client_name,"","packet 1",0);
			//sendChatToList(chatSock, &from, outPkt->_client_name, outPkt->_msgbuf,outPkt->_id);
			//delete(outPkt);

			//outPkt = createPkt(2,"", &from,inPkt._client_name,"","packet 2",0);
			//sendChatToList(chatSock, &from, outPkt->_client_name, outPkt->_msgbuf, outPkt->_id);
			//delete(outPkt);
			struct pkt* myPkt;
			struct sockaddr_in client;
			char chatData[QUERYSIZE];
			memset(chatData,0,QUERYSIZE);

			strncpy(chatData, "CHAT DATA: Sent From Receiver", 30);

			// Creating a pkt passing in the name of the user who sent the chat
			// UPDATE: use the following line when we update to use standalone chat server replacing the line below it.
			//	myPkt = createPkt(in_pktId,"",from, sentFromClientName, "", chatData,0);
			myPkt = createPkt(1,"",&from, inPkt._client_name, "", chatData,0);

			// Checksum the pkt!
			// performing the checksum and assigning it to the pkt
			myPkt->_checksum = performChecksum(myPkt);

			// Change myPkt port number to CLIENTCHATPORT (only needs to be done once)
			client.sin_port = htons(CLIENTCHATPORT);

			// Get the ip address from the list item and assign it to the client
			//   sockaddr_in struct variable
			client.sin_addr = inPkt._client.sin_addr;

			// Get the family type from the list item and assign it to the client
			//   sockaddr_in struct variable
			client.sin_family = inPkt._client.sin_family;

			if(rup_write(chatSock, myPkt, sizeof(struct pkt), &client) < 0)
			{
				if(errno == ENOBUFS)
					perror("Client Error: sending datagram");
				exit(1);
			}
		}
	}

	// WARNING: unreachable code!!!
	// Close the socket once we are done using it
	rup_close(chatSock);
}
